import {
	RenderingEngine,
	Enums,
	setVolumesForViewports,
	volumeLoader,
	getRenderingEngine,
	eventTarget,
	getEnabledElement,
	utilities as csUtils,
	cache,
	imageLoader,
	CONSTANTS
} from "@cornerstonejs/core";
import { Enums as toolsEnums, utilities } from "@cornerstonejs/tools";

import cornerstoneDICOMImageLoader from "@cornerstonejs/dicom-image-loader";
import * as cornerstoneTools from "@cornerstonejs/tools";

import { vec3 } from "gl-matrix";

// import vtkCellPicker from "@/libs/@kitware/vtk.js/Rendering/Core/CellPicker";
// import vtkActor from "@/libs/@kitware/vtk.js/Rendering/Core/Actor";
// import vtkSphereSource from "@/libs/@kitware/vtk.js/Filters/Sources/SphereSource";
// import vtkMapper from "@/libs/@kitware/vtk.js/Rendering/Core/Mapper";
import vtkAnnotatedCubeActor from "@kitware/vtk.js/Rendering/Core/AnnotatedCubeActor";
import vtkOrientationMarkerWidget from "@kitware/vtk.js/Interaction/Widgets/OrientationMarkerWidget";
import vtkOutlineFilter from "@kitware/vtk.js/Filters/General/OutlineFilter";
import vtkMapper from "@kitware/vtk.js/Rendering/Core/Mapper";
import vtkActor from "@kitware/vtk.js/Rendering/Core/Actor";

import ZoomMouseWheelTool from "./ZoomMouseWheelTool";
import VolumeShiftColorTool from "./VolumeShiftColorTool";

import { readTagAsString, decodeChinese, desensitizeSubstring } from "@/utils";
import { renderingEngineId } from "../cornerstone3D";
import MyProbeTool from "./MyProbeTool";
import CTRTool from "./CTRTool";

const {
	ToolGroupManager,
	Enums: csToolsEnums,
	synchronizers,
	SynchronizerManager,
	CrosshairsTool,
	TrackballRotateTool,
	StackScrollTool,
	WindowLevelTool,
	PanTool,
	ZoomTool,
	ProbeTool,
	LengthTool,
	AngleTool,
	RectangleROITool,
	EllipticalROITool,
	PlanarFreehandROITool,
	CobbAngleTool,
	ArrowAnnotateTool,
	LabelTool,
	EraserTool,
	OrientationMarkerTool
} = cornerstoneTools;

const toolsNames = [
	ProbeTool.toolName,
	MyProbeTool.toolName,
	LengthTool.toolName,
	AngleTool.toolName,
	RectangleROITool.toolName,
	EllipticalROITool.toolName,
	PlanarFreehandROITool.toolName,
	CobbAngleTool.toolName,
	CTRTool.toolName,
	ArrowAnnotateTool.toolName,
	LabelTool.toolName,
	EraserTool.toolName
];

const {
	createSlabThicknessSynchronizer,
	createCameraPositionSynchronizer,
	createVOISynchronizer
} = synchronizers;

const { MouseBindings, KeyboardBindings } = csToolsEnums;
const { ViewportType } = Enums;
const { IMAGE_RENDERED, CAMERA_MODIFIED, VOI_MODIFIED } = Enums.Events;
const { getOrientationStringLPS, invertOrientationStringLPS } = utilities.orientation;
const { MAXIMUM_INTENSITY_BLEND, MINIMUM_INTENSITY_BLEND, AVERAGE_INTENSITY_BLEND } =
	Enums.BlendModes;

const ViewportId = Object.freeze({
	AXIAL: "DICOM3D_AXIAL",
	SAGITTAL: "DICOM3D_SAGITTAL",
	CORONAL: "DICOM3D_CORONAL",
	VOLUME: "DICOM3D_VOLUME"
});

const idAxial = ViewportId.AXIAL;
const idSagittal = ViewportId.SAGITTAL;
const idCoronal = ViewportId.CORONAL;
const idVolume = ViewportId.VOLUME;

const volumeId = "DICOM3D_VOLUME_ID";
const viewportIds = [idAxial, idSagittal, idCoronal, idVolume];
const mprViewportIds = [idAxial, idSagittal, idCoronal];
// const renderingEngineId = "dicom3dRenderingEngine";
const synchronizerId = "SLAB_THICKNESS_SYNCHRONIZER_ID";
const cameraSynchronizerId = "CAMERA_SYNCHRONIZER_ID";
const voiSynchronizerId = "VOI_SYNCHRONIZER_ID";
const toolGroupId = "MPR_TOOLGROUP_ID";
const vrToolGroupId = "VR_TOOL_GROUP_ID";
let outlineActor = null;

const initVolumeCamera = {
	viewUp: [],
	viewPlaneNormal: [],
	position: [],
	focalPoint: [],
	viewAngle: 0,
	parallelScale: 1
};

const presetColors = CONSTANTS.VIEWPORT_PRESETS.map(preset => preset.name);

const blendModes = {
	MIP: MAXIMUM_INTENSITY_BLEND,
	MinIP: MINIMUM_INTENSITY_BLEND,
	AvgIP: AVERAGE_INTENSITY_BLEND
};

const cameraPos = {
	A: {
		viewPlaneNormal: [0, -1, 0],
		viewUp: [0, 0, 1]
	},
	P: {
		viewPlaneNormal: [0, 1, 0],
		viewUp: [0, 0, 1]
	},
	L: {
		viewPlaneNormal: [1, 0, 0],
		viewUp: [0, 0, 1]
	},
	R: {
		viewPlaneNormal: [-1, 0, 0],
		viewUp: [0, 0, 1]
	},
	S: {
		viewPlaneNormal: [0, 0, 1],
		viewUp: [0, 1, 0]
	},
	I: {
		viewPlaneNormal: [0, 0, -1],
		viewUp: [0, -1, 0]
	}
};

// Create synchronizers
// const synchronizer = createSlabThicknessSynchronizer(synchronizerId);
// createCameraPositionSynchronizer(cameraSynchronizerId);
createVOISynchronizer(voiSynchronizerId, {
	syncInvertState: false,
	syncColormap: false
});

const viewportColors = {
	[idAxial]: "rgb(135, 206, 250)",
	[idSagittal]: "rgb(255, 0, 0)",
	[idCoronal]: "rgb(0, 255, 0)",
	[idVolume]: "rgb(128, 128, 128)"
};

const axesConfig = {
	orientationWidget: {
		viewportSize: 0.08,
		minPixelSize: 70,
		maxPixelSize: 200
	},
	// overlayMarkerType: OrientationMarkerTool.OVERLAY_MARKER_TYPES.ANNOTATED_CUBE,
	overlayMarkerType: OrientationMarkerTool.OVERLAY_MARKER_TYPES.ANNOTATED_CUBE,
	// OrientationMarkerTool.OVERLAY_MARKER_TYPES.AXES
	overlayConfiguration: {
		[OrientationMarkerTool.OVERLAY_MARKER_TYPES.ANNOTATED_CUBE]: {
			faceProperties: {
				xPlus: {
					text: "L",
					faceColor: viewportColors[idSagittal], //"#ffff00",
					faceRotation: 90
				},
				xMinus: {
					text: "R",
					faceColor: viewportColors[idSagittal], //"#ffff00",
					faceRotation: 270
				},
				yPlus: {
					text: "P",
					faceColor: viewportColors[idCoronal], //"#00ffff",
					fontColor: "black",
					faceRotation: 180
				},
				yMinus: {
					text: "A",
					faceColor: viewportColors[idCoronal], //"#00ffff",
					fontColor: "black"
				},
				zPlus: {
					text: "S",
					faceColor: viewportColors[idAxial] //"#00ffff",
					// fontColor: "white",
				},
				zMinus: {
					text: "I",
					faceColor: viewportColors[idAxial] //"#00ffff",
					// fontColor: "white",
				}
			},
			defaultStyle: {
				fontStyle: "bold",
				fontFamily: "Arial",
				fontColor: "black",
				fontSizeScale: res => res / 2,
				faceColor: "#0000ff",
				edgeThickness: 0.05,
				edgeColor: "black",
				resolution: 400
			}
		},
		[OrientationMarkerTool.OVERLAY_MARKER_TYPES.AXES]: {},
		[OrientationMarkerTool.OVERLAY_MARKER_TYPES.CUSTOM]: {
			polyDataURL: "/src/assets/Human.vtp"
		}
	}
};

// const viewportReferenceLineControllable = [idAxial, idSagittal, idCoronal, idVolume];
// const viewportReferenceLineDraggableRotatable = [idAxial, idSagittal, idCoronal, idVolume];
// const viewportReferenceLineSlabThicknessControlsOn = [idAxial, idSagittal, idCoronal, idVolume];

function getReferenceLineColor(viewportId) {
	return viewportColors[viewportId];
}

function getReferenceLineControllable(viewportId) {
	// const index = viewportReferenceLineControllable.indexOf(viewportId);
	const index = viewportIds.indexOf(viewportId);
	return index !== -1;
}

function getReferenceLineDraggableRotatable(viewportId) {
	// const index = viewportReferenceLineDraggableRotatable.indexOf(viewportId);
	const index = viewportIds.indexOf(viewportId);
	return index !== -1;
}
function getReferenceLineSlabThicknessControlsOn(viewportId) {
	// const index = viewportReferenceLineSlabThicknessControlsOn.indexOf(viewportId);
	const index = viewportIds.indexOf(viewportId);
	return index !== -1;
}

function prepareImageDataForPicking(viewport) {
	const volumeActor = viewport.getDefaultActor().actor;
	if (!volumeActor) {
		return;
	}
	// Get the imageData from the volumeActor
	const imageData = volumeActor.getMapper().getInputData();

	if (!imageData) {
		console.error("No imageData found in the volumeActor");
		return null;
	}

	// Get the voxelManager from the imageData
	const { voxelManager } = imageData.get("voxelManager");

	if (!voxelManager) {
		console.error("No voxelManager found in the imageData");
		return imageData;
	}

	// Create a fake scalar object to expose the scalar data to VTK.js
	const fakeScalars = {
		getData: () => {
			return voxelManager.getCompleteScalarDataArray();
		},
		getNumberOfComponents: () => voxelManager.numberOfComponents,
		getDataType: () => voxelManager.getCompleteScalarDataArray().constructor.name
	};

	// Set the point data to return the fakeScalars
	imageData.setPointData({
		getScalars: () => fakeScalars
	});
}

export default class MPR {
	constructor(params) {
		this.toolGroup = null;
		this.vrToolGroup = null;
		this.renderingEngine = null;
		this.registered = false;
		this.viewportInputArray = null;
		this.crosshairsToolActive = true;
		this.loaded = false;
		this.selecteToolName = "";
		this.params = params;
		this.volume = null;
		this.init(params);
	}

	init(config = {}) {
		const { elAxial, elSagittal, elCoronal, elVR } = config;

		cornerstoneTools.addTool(CrosshairsTool);
		cornerstoneTools.addTool(TrackballRotateTool);
		cornerstoneTools.addTool(ZoomTool);
		cornerstoneTools.addTool(PanTool);
		cornerstoneTools.addTool(WindowLevelTool);
		cornerstoneTools.addTool(OrientationMarkerTool);
		// cornerstoneTools.addTool(ZoomMouseWheelTool);
		cornerstoneTools.addTool(VolumeShiftColorTool);

		this.vrToolGroup = ToolGroupManager.getToolGroup(vrToolGroupId);
		if (!this.vrToolGroup) {
			this.vrToolGroup = ToolGroupManager.createToolGroup(vrToolGroupId);
			this.vrToolGroup.addTool(TrackballRotateTool.toolName);
			this.vrToolGroup.addTool(ZoomTool.toolName, {
				zoomToCenter: true,
				invert: false,
				minZoomScale: 0.15,
				maxZoomScale: 20
			});

			// this.vrToolGroup.addTool(ZoomMouseWheelTool.toolName, {
			// 	factor: 0.1,
			// 	minZoomScale: 0.1,
			// 	maxZoomScale: 30,
			// 	invert: false
			// });

			this.vrToolGroup.addTool(VolumeShiftColorTool.toolName);

			this.vrToolGroup.addTool(PanTool.toolName);
			this.vrToolGroup.addTool(OrientationMarkerTool.toolName, axesConfig);

			this.vrToolGroup.setToolActive(TrackballRotateTool.toolName, {
				bindings: [
					{
						mouseButton: MouseBindings.Primary // Left Click
					}
				]
			});

			this.vrToolGroup.setToolActive(ZoomTool.toolName, {
				bindings: [
					{
						// mouseButton: MouseBindings.Secondary
						mouseButton: MouseBindings.Wheel
					}
				]
			});

			this.vrToolGroup.setToolActive(VolumeShiftColorTool.toolName, {
				bindings: [
					{
						mouseButton: MouseBindings.Secondary // Right Click
					}
				]
			});

			// cornerstonejs 3.15.3版本ZoolTool中已经有了鼠标滚轮缩放功能，不需要再单独添加
			// this.vrToolGroup.setToolActive(ZoomMouseWheelTool.toolName, {
			// 	bindings: [
			// 		{
			// 			mouseButton: MouseBindings.Wheel
			// 		}
			// 	]
			// });

			this.vrToolGroup.setToolActive(PanTool.toolName, {
				bindings: [
					{
						mouseButton: MouseBindings.Auxiliary
					}
				]
			});
		}

		// Instantiate a rendering engine
		this.renderingEngine = new RenderingEngine(renderingEngineId);

		this.viewportInputArray = [
			{
				viewportId: idAxial,
				type: ViewportType.ORTHOGRAPHIC,
				element: elAxial,
				defaultOptions: {
					orientation: Enums.OrientationAxis.AXIAL,
					background: [0, 0, 0]
				}
			},
			{
				viewportId: idSagittal,
				type: ViewportType.ORTHOGRAPHIC,
				element: elSagittal,
				defaultOptions: {
					orientation: Enums.OrientationAxis.SAGITTAL,
					background: [0, 0, 0]
				}
			},
			{
				viewportId: idCoronal,
				type: ViewportType.ORTHOGRAPHIC,
				element: elCoronal,
				defaultOptions: {
					orientation: Enums.OrientationAxis.CORONAL,
					background: [0, 0, 0]
				}
			},
			{
				viewportId: idVolume,
				type: ViewportType.VOLUME_3D,
				element: elVR,
				defaultOptions: {
					background: [0, 0, 0],
					orientation: Enums.OrientationAxis.CORONAL
					// orientation: Enums.OrientationAxis.AXIAL
					// orientation: Enums.OrientationAxis.SAGITTAL
				}
			}
		];

		this.renderingEngine.setViewports(this.viewportInputArray);

		// Define tool groups to add the segmentation display tool to
		this.toolGroup = ToolGroupManager.getToolGroup(toolGroupId);
		if (!this.toolGroup) {
			this.toolGroup = ToolGroupManager.createToolGroup(toolGroupId);
			// this.toolGroup.addTool(OrientationMarkerTool.toolName, axesConfig);
			this.toolGroup.addTool(WindowLevelTool.toolName);

			const isMobile = window.matchMedia("(any-pointer:coarse)").matches;

			this.toolGroup.addTool(CrosshairsTool.toolName, {
				getReferenceLineColor,
				getReferenceLineControllable,
				getReferenceLineDraggableRotatable,
				getReferenceLineSlabThicknessControlsOn,
				referenceLinesCenterGapRadius: 14,
				viewportIndicators: false,
				mobile: {
					enabled: isMobile,
					opacity: 0.8,
					handleRadius: 9
				}
			});

			this.addManipulationBindings(this.toolGroup);
		}

		// For the crosshairs to operate, the viewports must currently be
		// added ahead of setting the tool active. This will be improved in the future.
		this.toolGroup.addViewport(idAxial, renderingEngineId);
		this.toolGroup.addViewport(idSagittal, renderingEngineId);
		this.toolGroup.addViewport(idCoronal, renderingEngineId);
		this.vrToolGroup.addViewport(idVolume, renderingEngineId);

		// Manipulation Tools
		// Add Crosshairs tool and configure it to link the three viewports
		// These viewports could use different tool groups. See the PET-CT example
		// for a more complicated used case.
		// this.setUpSynchronizers();
	}

	destroy() {
		this.renderingEngine.disableElement(idVolume);
		this.renderingEngine.disableElement(idAxial);
		this.renderingEngine.disableElement(idSagittal);
		this.renderingEngine.disableElement(idCoronal);

		this.toolGroup.removeViewports(renderingEngineId);
		this.vrToolGroup.removeViewports(renderingEngineId);
		this.renderingEngine.destroy();
		cache.purgeCache();
	}

	addManipulationBindings(toolGroup, options = {}) {
		const zoomBindings = [
			{
				mouseButton: MouseBindings.Secondary
			}
		];

		const {
			is3DViewport = false,
			enableShiftClickZoom = false,
			toolMap = new Map()
		} = options;

		if (enableShiftClickZoom === true) {
			zoomBindings.push({
				mouseButton: MouseBindings.Primary, // Shift Left Click
				modifierKey: KeyboardBindings.Shift
			});
		}

		if (!this.registered) {
			// cornerstoneTools.addTool(PanTool);
			// cornerstoneTools.addTool(ZoomTool);
			// cornerstoneTools.addTool(TrackballRotateTool);
			// cornerstoneTools.addTool(StackScrollTool);
			// cornerstoneTools.addTool(ProbeTool);
			// cornerstoneTools.addTool(LengthTool);
			// cornerstoneTools.addTool(AngleTool);
			// cornerstoneTools.addTool(RectangleROITool);
			// cornerstoneTools.addTool(EllipticalROITool);
			// cornerstoneTools.addTool(PlanarFreehandROITool);
			// cornerstoneTools.addTool(CobbAngleTool);
			// cornerstoneTools.addTool(ArrowAnnotateTool);
			// cornerstoneTools.addTool(LabelTool);
			// cornerstoneTools.addTool(EraserTool);
			for (const [, config] of toolMap) {
				if (config.tool) {
					cornerstoneTools.addTool(config.tool);
				}
			}
		}

		this.registered = true;

		toolGroup.addTool(PanTool.toolName);
		toolGroup.addTool(ZoomTool.toolName, {
			zoomToCenter: true,
			invert: true,
			minZoomScale: 0.1,
			maxZoomScale: 20
		});
		if (is3DViewport) {
			toolGroup.addTool(TrackballRotateTool.toolName);
		} else {
			toolGroup.addTool(StackScrollTool.toolName);
		}

		toolGroup.addTool(StackScrollTool.toolName);

		toolGroup.setToolActive(PanTool.toolName, {
			bindings: [
				{
					mouseButton: MouseBindings.Auxiliary
				},
				{
					numTouchPoints: 1,
					modifierKey: KeyboardBindings.Ctrl
				}
			]
		});
		toolGroup.setToolActive(ZoomTool.toolName, {
			bindings: zoomBindings
		});
		// Need a binding to navigate without a wheel mouse
		toolGroup.setToolActive(StackScrollTool.toolName, {
			bindings: [
				{
					mouseButton: MouseBindings.Primary,
					modifierKey: KeyboardBindings.Alt
				},
				{
					numTouchPoints: 1,
					modifierKey: KeyboardBindings.Alt
				},
				{
					mouseButton: MouseBindings.Wheel
				}
			]
		});
		// Add a length tool binding to allow testing annotations on examples targetting
		// other use cases.  Use a primary button with shift+ctrl as that is relatively
		// unlikely to be otherwise used.
		toolGroup.setToolActive(LengthTool.toolName, {
			bindings: [
				{
					mouseButton: MouseBindings.Primary,
					modifierKey: KeyboardBindings.ShiftCtrl
				},
				{
					numTouchPoints: 1,
					modifierKey: KeyboardBindings.ShiftCtrl
				}
			]
		});

		if (is3DViewport) {
			toolGroup.setToolActive(TrackballRotateTool.toolName, {
				bindings: [
					{
						mouseButton: MouseBindings.Primary
					}
				]
			});
		} else {
			toolGroup.setToolActive(StackScrollTool.toolName);
		}

		toolGroup.addTool(MyProbeTool.toolName, {
			handleRadius: "4"
		});
		toolGroup.addTool(ProbeTool.toolName, {
			handleRadius: "4"
		});
		toolGroup.addTool(LengthTool.toolName);
		toolGroup.addTool(AngleTool.toolName);
		toolGroup.addTool(RectangleROITool.toolName);
		toolGroup.addTool(EllipticalROITool.toolName);
		toolGroup.addTool(PlanarFreehandROITool.toolName);
		toolGroup.addTool(CobbAngleTool.toolName);
		toolGroup.addTool(CTRTool.toolName);
		toolGroup.addTool(ArrowAnnotateTool.toolName);
		toolGroup.addTool(LabelTool.toolName);
		toolGroup.addTool(EraserTool.toolName);

		// Add extra tools from the toolMap
		for (const [toolName, config] of toolMap) {
			if (config.baseTool) {
				if (!toolGroup.hasTool(config.baseTool)) {
					toolGroup.addTool(
						config.baseTool,
						toolMap.get(config.baseTool)?.configuration
					);
				}
				toolGroup.addToolInstance(
					toolName,
					config.baseTool,
					config.configuration
				);
			} else if (!toolGroup.hasTool(toolName)) {
				toolGroup.addTool(toolName, config.configuration);
			}
			if (config.passive) {
				// This can be applied during add/remove contours
				toolGroup.setToolPassive(toolName);
			}
			if (config.bindings || config.selected) {
				toolGroup.setToolActive(
					toolName,
					(config.bindings && config) || {
						bindings: [{ mouseButton: MouseBindings.Primary }]
					}
				);
			}
		}
	}

	setUpSynchronizers() {
		// this.synchronizer = createSlabThicknessSynchronizer(synchronizerId);

		// Add viewports to VOI synchronizers
		mprViewportIds.forEach(viewportId => {
			synchronizer.add({
				renderingEngineId,
				viewportId
			});
		});
		// Normally this would be left on, but here we are starting the demo in the
		// default state, which is to not have a synchronizer enabled.
		synchronizer.setEnabled(false);
	}

	async loadImages(imageIds) {
		let distinctImageIds = [...new Set(imageIds)];

		// 2025.05.26 限制三维重建最大图像数为220张
		const maxImageCount = 220;
		const imageCount = distinctImageIds.length;
		const step = Math.floor(imageCount / maxImageCount) + 1;
		let newImageIds = [];
		for (let i = 0; i < imageCount; i += step) {
			newImageIds.push(distinctImageIds[i]);
		}

		console.log("loadImages, imageIds:", newImageIds.length, distinctImageIds.length);

		for (let i = 0; i < newImageIds.length; i++) {
			await cornerstoneDICOMImageLoader.wadouri.loadImage(newImageIds[i]).promise;
		}

		// Define a volume in memory
		this.volume = await volumeLoader.createAndCacheVolume(volumeId, {
			imageIds: newImageIds
		});

		// console.log("Volume loaded:", volumeId, volume);
		// console.log("viewportInputArray:", viewportInputArray);
		// this.renderingEngine.setViewports(this.viewportInputArray);

		this.volume.load();
		// console.log("loadImages, volume", volume);

		// Set volumes on the viewports
		await setVolumesForViewports(
			this.renderingEngine,
			[
				{
					volumeId,
					callback: null
				}
			],
			viewportIds
		);

		this.toolGroup.setToolActive(CrosshairsTool.toolName, {
			bindings: [{ mouseButton: MouseBindings.Primary }]
		});

		// const picker = vtkCellPicker.newInstance({
		// 	opacityThreshold: 0.0001,
		// });
		// picker.setPickFromList(1);
		// picker.setTolerance(0);
		// picker.initializePickList();
		// Render the image
		this.renderingEngine.renderViewports(viewportIds);

		const viewport = this.renderingEngine.getViewport(idVolume);
		await setVolumesForViewports(
			this.renderingEngine,
			[{ volumeId }],
			[idVolume]
		).then(() => {
			viewport.setProperties({
				preset: "CT-Coronary-Arteries-2"
			});
			const camera = viewport.getCamera();
			initVolumeCamera.viewUp = camera.viewUp;
			initVolumeCamera.viewPlaneNormal = camera.viewPlaneNormal;
			initVolumeCamera.position = camera.position;
			initVolumeCamera.focalPoint = camera.focalPoint;
			initVolumeCamera.viewAngle = camera.viewAngle;
			initVolumeCamera.parallelScale = camera.parallelScale;
			// const defaultActor = viewport.getDefaultActor();
			// if (defaultActor.actor) {
			// 	// Cast to any to avoid type errors with different actor types
			// 	picker.addPickList(defaultActor.actor);
			// 	prepareImageDataForPicking(viewport);
			// }
			// viewport.render();
		});

		// this.vrToolGroup.setToolEnabled(OrientationMarkerTool.toolName, axesConfig);

		// this.addPlanes();
		this.addOutline();
		// this.addAxes();

		this.showAxes(true);

		this.loaded = true;
	}
	setWindow(ww, wl) {
		// const renderingEngine = getRenderingEngine(renderingEngineId);
		if (!this.loaded) {
			return;
		}

		const viewports = this.renderingEngine.getViewports();
		for (let i = 0; i < viewports.length; i++) {
			const viewport = viewports[i];
			if (viewport.id !== idVolume) {
				const voiLutFunction = viewport.getProperties().VOILUTFunction;
				const newRange = csUtils.windowLevel.toLowHighRange(
					ww,
					wl,
					voiLutFunction
				);

				viewport.setProperties({
					voiRange: newRange
				});
				viewport.render();
			}
		}
	}

	getWindow(viewportId) {
		if (!this.loaded) {
			return;
		}

		const viewport = this.renderingEngine.getViewport(viewportId);
		const range = viewport.getProperties().voiRange;
		return csUtils.windowLevel.toWindowLevel(range.lower, range.upper);
	}
	getSliceCount(viewportId) {
		if (!this.loaded) {
			return;
		}

		const viewport = this.renderingEngine.getViewport(viewportId);
		return viewport.getNumberOfSlices();
	}
	getSliceIndex(viewportId) {
		if (!this.loaded) {
			return;
		}

		const viewport = this.renderingEngine.getViewport(viewportId);
		return viewport.getSliceIndex();
	}

	getSlabThickness(viewportId) {
		if (!this.loaded) {
			return 0;
		}

		const viewport = this.renderingEngine.getViewport(viewportId);
		const slabThickness = viewport.getSlabThickness();
		return slabThickness;
	}
	setVRColor(presetName) {
		// const renderingEngine = getRenderingEngine(renderingEngineId);
		if (!this.loaded) {
			return;
		}

		const viewport = this.renderingEngine.getViewport(idVolume);
		if (viewport) {
			viewport.setProperties({ preset: presetName });
			viewport.render();
		}

		const { preset } = viewport.getProperties();
		preset.shiftPos = 0;
		// console.log("setVRColor preset:", preset);
	}

	shiftVRColor(pos) {
		if (!this.loaded) {
			return;
		}

		const viewport = this.renderingEngine.getViewport(idVolume);
		const volumeActor = viewport.getDefaultActor().actor;

		// const preset = CONSTANTS.VIEWPORT_PRESETS.find(preset => preset.name === currentPreset);
		const { preset } = viewport.getProperties();
		console.log("preset:", preset.shiftPos);

		const color = preset.colorTransfer.split(" ").map(Number);
		for (let i = 1; i < color.length; i += 4) {
			color[i] = color[i] + pos;
		}

		// const newColor = color.join(" ");
		// console.log("new color:", newColor);

		const opacity = preset.scalarOpacity.split(" ").map(Number);
		for (let i = 1; i < opacity.length; i += 2) {
			opacity[i] = opacity[i] + pos;
		}

		// const newOpacity = opacity.join(" ");
		// console.log("new opacity:", newOpacity);

		const newPreset = { ...preset };
		newPreset.colorTransfer = color.join(" ");
		newPreset.scalarOpacity = opacity.join(" ");

		csUtils.applyPreset(volumeActor, newPreset);
		preset.shiftPos = pos;

		viewport.render();
	}

	enableVolumeShiftColor(enable) {
		if (enable) {
			// this.vrToolGroup.setToolDisabled(TrackballRotateTool.toolName);
			this.vrToolGroup.setToolDisabled(ZoomTool.toolName);
			this.vrToolGroup.setToolActive(VolumeShiftColorTool.toolName, {
				bindings: [
					{
						mouseButton: MouseBindings.Secondary // Right Click
					}
				]
			});
		} else {
			this.vrToolGroup.setToolDisabled(VolumeShiftColorTool.toolName);
			// this.vrToolGroup.setToolEnabled(ZoomTool.toolName);
			this.vrToolGroup.setToolActive(ZoomTool.toolName, {
				bindings: [
					{
						mouseButton: MouseBindings.Secondary // Right Click
					}
				]
			});

			// this.vrToolGroup.setToolActive(TrackballRotateTool.toolName, {
			// 	bindings: [
			// 		{
			// 			mouseButton: MouseBindings.Primary // Left Click
			// 		}
			// 	]
			// });
		}
	}

	setBlendMode(mode) {
		// const renderingEngine = getRenderingEngine(renderingEngineId);
		if (!this.loaded) {
			return;
		}

		// const modeValue = blendModes.get(mode);
		const modeValue = blendModes[mode];

		const crosshairsInstance = this.toolGroup.getToolInstance(
			CrosshairsTool.toolName
		);
		const oldConfiguration = crosshairsInstance.configuration;

		console.log("setBlendMode, oldConfiguration:", oldConfiguration);

		crosshairsInstance.configuration = {
			...oldConfiguration,
			slabThicknessBlendMode: modeValue
		};

		// console.log("Setting blend mode to:", modeValue);

		mprViewportIds.forEach(viewportId => {
			const viewport = this.renderingEngine.getViewport(viewportId);
			viewport.setBlendMode(modeValue);
			viewport.render();
		});
		// const viewports = this.renderingEngine.getViewports();
		// for (let i = 0; i < viewports.length; i++) {
		// 	const viewport = viewports[i];
		// 	if (viewport.id !== idVolume) {
		// 		viewport.setBlendMode(modeValue);
		// 		viewport.render();
		// 	}
		// }
	}
	resize(viewIds, maxViewId) {
		if (!this.loaded) return;

		this.renderingEngine.resize();
		viewIds.forEach(viewportId => {
			const viewport = this.renderingEngine.getViewport(viewportId);
			if (viewport) {
				const presentation = viewport.getViewPresentation();
				viewport.setViewPresentation(presentation);
			}
		});

		if (maxViewId) {
			this.toolGroup.setToolDisabled(CrosshairsTool.toolName);
			this.toolGroup.setToolEnabled(WindowLevelTool.toolName);
			this.toolGroup.setToolActive(WindowLevelTool.toolName, {
				bindings: [
					{
						mouseButton: MouseBindings.Primary
					}
				]
			});
		} else {
			this.toolGroup.setToolEnabled(CrosshairsTool.toolName);
			this.toolGroup.setToolDisabled(WindowLevelTool.toolName);
			this.toolGroup.setToolActive(CrosshairsTool.toolName, {
				bindings: [{ mouseButton: MouseBindings.Primary }]
			});

			this.vrToolGroup.setToolEnabled(OrientationMarkerTool.toolName, axesConfig);
			// this.toolGroup.setToolActive(
			// 	OrientationMarkerTool.toolName,
			// 	axesConfig
			// );
		}
	}
	bindRenderEvent(callback) {
		for (let i = 0; i < this.viewportInputArray.length; i++) {
			if (this.viewportInputArray[i].viewportId !== idVolume) {
				const elem = this.viewportInputArray[i].element;
				elem.addEventListener(IMAGE_RENDERED, callback);
			}
		}
	}
	bindCameraEvent(callback) {
		for (let i = 0; i < this.viewportInputArray.length; i++) {
			if (this.viewportInputArray[i].viewportId !== idVolume) {
				const elem = this.viewportInputArray[i].element;
				elem.addEventListener(CAMERA_MODIFIED, callback);
			}
		}
	}
	toggleCrosshairs() {
		if (this.crosshairsToolActive === true) {
			this.crosshairsToolActive = false;
			this.toolGroup.setToolDisabled(CrosshairsTool.toolName);
			// this.toolGroup.setToolEnabled(WindowLevelTool.toolName);
			this.toolGroup.setToolActive(WindowLevelTool.toolName, {
				bindings: [
					{
						mouseButton: MouseBindings.Primary
					}
				]
			});
		} else {
			this.crosshairsToolActive = true;
			this.toolGroup.setToolDisabled(WindowLevelTool.toolName);
			// this.toolGroup.setToolEnabled(CrosshairsTool.toolName);
			this.toolGroup.setToolActive(CrosshairsTool.toolName, {
				bindings: [{ mouseButton: MouseBindings.Primary }]
			});
		}
	}
	enableCrosshairs() {
		if (this.crosshairsToolActive === false) {
			this.crosshairsToolActive = true;
			// this.toolGroup.setToolEnabled(CrosshairsTool.toolName);
			this.toolGroup.setToolActive(CrosshairsTool.toolName, {
				bindings: [{ mouseButton: MouseBindings.Primary }]
			});
		}
	}
	disableCrosshairs() {
		if (this.crosshairsToolActive === true) {
			this.crosshairsToolActive = false;
			this.toolGroup.setToolDisabled(CrosshairsTool.toolName);
		}
	}
	setMeasureType(type) {
		console.log("setMeasureType", type, this.selecteToolName);

		if (!type) {
			this.disableMeasureTool(this.selecteToolName);
			this.enableCrosshairs();
			this.selecteToolName = "";
		} else {
			let toolName = type;
			if (type === "Label-L" || type === "Label-R") {
				toolName = LabelTool.toolName;
			}

			this.toolGroup.setToolDisabled(WindowLevelTool.toolName);
			this.disableCrosshairs();
			this.toolGroup.setToolPassive(this.selectedToolName);

			this.toolGroup.setToolActive(toolName, {
				bindings: [{ mouseButton: MouseBindings.Primary }]
			});

			if (type === "Label-L" || type === "Label-R") {
				const text = type.split("-")[1];
				this.toolGroup.setToolConfiguration(LabelTool.toolName, {
					getTextCallback: fn => {
						return fn(text);
					}
				});
			} else if (type === "Label") {
				this.toolGroup.setToolConfiguration(LabelTool.toolName, {
					getTextCallback: fn => {
						return fn(prompt("Enter your annotation:"));
					}
				});
			}

			this.selectedToolName = toolName;
		}
		// switch (type) {
		// 	case "measure-none":
		// 		this.disableMeasureTool(this.selecteToolName);
		// 		this.enableCrosshairs();
		// 		this.selecteToolName = "";
		// 		break;
		// 	case "measure-point":
		// 		this.enableMeasureTool(ProbeTool.toolName);
		// 		break;
		// 	case "measure-line":
		// 		this.enableMeasureTool(LengthTool.toolName);
		// 		break;
		// 	case "measure-angle":
		// 		this.enableMeasureTool(AngleTool.toolName);
		// 		break;
		// 	case "measure-rect":
		// 		this.enableMeasureTool(RectangleROITool.toolName);
		// 		break;
		// 	case "measure-ellipse":
		// 		this.enableMeasureTool(EllipticalROITool.toolName);
		// 		break;
		// 	case "measure-curve":
		// 		this.enableMeasureTool(PlanarFreehandROITool.toolName);
		// 		break;
		// 	// case "measure-ctr":
		// 	case "measure-cobbangle":
		// 		this.enableMeasureTool(CobbAngleTool.toolName);
		// 		break;
		// 	// case "measure-left":
		// 	// case "measure-right":
		// 	case "measure-arrow":
		// 		this.enableMeasureTool(ArrowAnnotateTool.toolName);
		// 		break;
		// 	case "measure-text":
		// 		this.enableMeasureTool(LabelTool.toolName);
		// 		break;
		// 	case "measure-erase":
		// 		this.enableMeasureTool(EraserTool.toolName);
		// 		break;
		// }
	}
	enableMeasureTool(toolName) {
		if (this.selecteToolName === toolName) {
			return;
		}

		this.toolGroup.setToolDisabled(WindowLevelTool.toolName);
		this.disableCrosshairs();
		for (let i = 0; i < toolsNames.length; i++) {
			if (toolsNames[i] === toolName) {
				this.toolGroup.setToolActive(toolsNames[i], {
					bindings: [{ mouseButton: MouseBindings.Primary }]
				});
			} else {
				this.disableMeasureTool(this.selecteToolName);
			}
		}
		this.selecteToolName = toolName;
	}
	disableMeasureTool(toolName) {
		if (toolName) {
			this.toolGroup.setToolPassive(toolName);
		}
	}
	removeAnnotations() {
		const annotations = cornerstoneTools.annotation.state.getAnnotations(
			LengthTool.toolName,
			this.params.elAxial
		);

		for (const annotation of annotations) {
			cornerstoneTools.annotation.state.removeAnnotation(annotation.annotationUID);
		}
		// cornerstoneTools.annotation.state.removeAnnotations(
		// 	toolGroupId,
		// 	LengthTool.toolName
		// );
	}

	addPlanes() {
		const viewport = this.renderingEngine.getViewport(idAxial);
		const viewport3d = this.renderingEngine.getViewport(idVolume);

		viewport3d.addActor({
			uid: "PLANE_AXIAL",
			actor: viewport.getDefaultActor().actor
		});
		viewport3d.render();
	}
	addOutline() {
		// Create image outline in 3D view
		const outline = vtkOutlineFilter.newInstance();
		const mapper = vtkMapper.newInstance();
		outlineActor = vtkActor.newInstance();
		outlineActor.setMapper(mapper);

		outline.setInputData(this.volume.imageData);
		mapper.setInputData(outline.getOutputData());

		const viewport = this.renderingEngine.getViewport(idVolume);
		viewport.addActor({ uid: "VOLUME_OUTLINE", actor: outlineActor });
		outlineActor.setVisibility(false);
		viewport.render();
	}

	showOutline(show) {
		if (!outlineActor) return;
		outlineActor.setVisibility(show);
		const viewport = this.renderingEngine.getViewport(idVolume);
		viewport.render();
	}

	showAxes(show) {
		if (show) {
			this.vrToolGroup.setToolConfiguration(OrientationMarkerTool.toolName, {
				overlayMarkerType: axesConfig.overlayMarkerType
			});
			this.vrToolGroup.setToolEnabled(OrientationMarkerTool.toolName);
			// this.vrToolGroup.setToolActive(OrientationMarkerTool.toolName, axesConfig);
		} else {
			this.vrToolGroup.setToolDisabled(OrientationMarkerTool.toolName);
		}
	}

	setAxesType(type) {
		axesConfig.overlayMarkerType = type;
		const options = this.vrToolGroup.getToolOptions(OrientationMarkerTool.toolName);
		// console.log("setAxesType", type, options);
		if (options.mode === "Enabled") {
			this.vrToolGroup.setToolDisabled(OrientationMarkerTool.toolName);
			this.vrToolGroup.setToolConfiguration(OrientationMarkerTool.toolName, {
				overlayMarkerType: type
			});

			this.vrToolGroup.setToolEnabled(OrientationMarkerTool.toolName);
		}
	}

	addAxes() {
		const axes = vtkAnnotatedCubeActor.newInstance();
		axes.setDefaultStyle({
			fontStyle: "bold",
			fontFamily: "Arial",
			fontColor: "black",
			fontSizeScale: res => res / 2,
			faceColor: "black",
			edgeThickness: 0.1,
			edgeColor: "black",
			resolution: 400
		});
		axes.setXPlusFaceProperty({
			text: "L",
			faceColor: viewportColors[idSagittal],
			faceRotation: 90
		});
		axes.setXMinusFaceProperty({
			text: "R",
			faceColor: viewportColors[idSagittal],
			faceRotation: 270
		});
		axes.setYPlusFaceProperty({
			text: "P",
			faceColor: viewportColors[idCoronal],
			faceRotation: 180
		});
		axes.setYMinusFaceProperty({
			text: "A",
			faceColor: viewportColors[idCoronal]
		});
		axes.setZPlusFaceProperty({
			text: "S",
			faceColor: viewportColors[idAxial]
		});
		axes.setZMinusFaceProperty({
			text: "I",
			faceColor: viewportColors[idAxial]
		});

		const viewport = this.renderingEngine.getViewport(idVolume);
		const interactor = viewport.getRenderer().getRenderWindow().getInteractor();

		// create orientation widget
		const orientationWidget = vtkOrientationMarkerWidget.newInstance({
			actor: axes,
			interactor: interactor,
			parentRenderer: viewport.getRenderer()
		});

		orientationWidget.setEnabled(true);
		orientationWidget.setViewportCorner(
			vtkOrientationMarkerWidget.Corners.BOTTOM_RIGHT
		);
		orientationWidget.setViewportSize(0.08);
		orientationWidget.setMinPixelSize(50);
		orientationWidget.setMaxPixelSize(200);

		// viewport.addActor({ uid: "VOLUME_AXES", actor: axes });
		// viewport.addWidget("VOLUME_AXES", orientationWidget);
		viewport.render();
	}

	getOrientationMarkers({ camera, rotation }) {
		// const { rotation, previousCamera, camera } = detail;
		let flipVertical = camera.flipVertical || false;
		let flipHorizontal = camera.flipHorizontal || false;
		let newRotation = rotation || 0;

		let rowCosines, columnCosines;

		// const { viewUp, viewPlaneNormal } = viewport.getCamera();
		const { viewUp, viewPlaneNormal } = camera;

		const viewRight = vec3.create();
		vec3.cross(viewRight, viewUp, viewPlaneNormal);

		columnCosines = [-viewUp[0], -viewUp[1], -viewUp[2]];
		rowCosines = viewRight;

		const rowString = getOrientationStringLPS(rowCosines);
		const columnString = getOrientationStringLPS(columnCosines);
		const oppositeRowString = invertOrientationStringLPS(rowString);
		const oppositeColumnString = invertOrientationStringLPS(columnString);

		const markers = {
			top: oppositeColumnString,
			left: oppositeRowString,
			right: rowString,
			bottom: columnString
		};

		// If any vertical or horizontal flips are applied, change the orientation strings ahead of
		// the rotation applications
		if (flipVertical) {
			markers.top = invertOrientationStringLPS(markers.top);
			markers.bottom = invertOrientationStringLPS(markers.bottom);
		}

		if (flipHorizontal) {
			markers.left = invertOrientationStringLPS(markers.left);
			markers.right = invertOrientationStringLPS(markers.right);
		}

		// Swap the labels accordingly if the viewport has been rotated
		// This could be done in a more complex way for intermediate rotation values (e.g. 45 degrees)
		if (newRotation === 90 || newRotation === -270) {
			return {
				top: markers.left,
				left: invertOrientationStringLPS(markers.top),
				right: invertOrientationStringLPS(markers.bottom),
				bottom: markers.right // left
			};
		} else if (newRotation === -90 || newRotation === 270) {
			return {
				top: invertOrientationStringLPS(markers.left),
				left: markers.top,
				bottom: markers.left,
				right: markers.bottom
			};
		} else if (newRotation === 180 || newRotation === -180) {
			return {
				top: invertOrientationStringLPS(markers.top),
				left: invertOrientationStringLPS(markers.left),
				bottom: invertOrientationStringLPS(markers.bottom),
				right: invertOrientationStringLPS(markers.right)
			};
		}

		return markers;
	}
	enableSyncWindow() {
		const voiSynchronizer = SynchronizerManager.getSynchronizer(voiSynchronizerId);
		// const cameraSynchronizer =
		// 	SynchronizerManager.getSynchronizer(cameraSynchronizerId);

		mprViewportIds.forEach(viewportId => {
			if (voiSynchronizer) {
				voiSynchronizer.add({ renderingEngineId, viewportId });
			}

			// if (cameraSynchronizer) {
			// 	cameraSynchronizer.add({ renderingEngineId, viewportId });
			// }
		});
	}
	disableSyncWindow() {
		const voiSynchronizer = SynchronizerManager.getSynchronizer(voiSynchronizerId);
		// const cameraSynchronizer =
		// 	SynchronizerManager.getSynchronizer(cameraSynchronizerId);

		mprViewportIds.forEach(viewportId => {
			if (voiSynchronizer) {
				voiSynchronizer.remove({ renderingEngineId, viewportId });
			}

			// if (cameraSynchronizer) {
			// 	cameraSynchronizer.remove({ renderingEngineId, viewportId });
			// }
		});
	}

	syncZoom(viewportId, camera, preCamera) {
		for (let i = 0; i < mprViewportIds.length; i++) {
			const otherViewportId = mprViewportIds[i];
			if (viewportId !== otherViewportId) {
				const otherViewport = this.renderingEngine.getViewport(otherViewportId);
				if (otherViewport) {
					const deltaScale = camera.parallelScale - preCamera.parallelScale;

					const otherCamera = otherViewport.getCamera();
					otherCamera.parallelScale += deltaScale;

					otherViewport.setCamera(otherCamera);
					otherViewport.render();
				}
			}
		}
	}

	resetCamera() {
		if (!this.loaded) return;
		viewportIds.forEach(viewportId => {
			const viewport = this.renderingEngine.getViewport(viewportId);
			// console.log("resetCamera", viewportId);
			if (viewport) {
				if (viewportId === idVolume) {
					viewport.setProperties({
						preset: "CT-Coronary-Arteries-2"
					});

					const camera = viewport.getCamera();
					camera.viewUp = initVolumeCamera.viewUp;
					camera.viewPlaneNormal = initVolumeCamera.viewPlaneNormal;
					camera.position = initVolumeCamera.position;
					camera.focalPoint = initVolumeCamera.focalPoint;
					camera.viewAngle = initVolumeCamera.viewAngle;
					camera.parallelScale = initVolumeCamera.parallelScale;
					// console.log("vr resetCamera", camera);
					viewport.setCamera(camera);

					// viewport.render();
				} else {
					viewport.resetCamera();
					// viewport.resetProperties();
					viewport.render();
				}
			}
		});
	}
	setCameraPosition(position) {
		const data = cameraPos[position];
		// console.log("setCameraPosition", position, data);
		const viewport = this.renderingEngine.getViewport(idVolume);
		if (viewport) {
			// const camera = viewport.getCamera();
			// camera.viewUp = data.viewUp;
			// camera.viewPlaneNormal = data.viewPlaneNormal;
			// viewport.setCamera(camera);

			viewport.setCamera({
				viewUp: data.viewUp,
				viewPlaneNormal: data.viewPlaneNormal
			});

			viewport.resetCamera();
			// viewport.render();
		}
	}
}

function loadDicom(imageId) {
	return cornerstoneDICOMImageLoader.wadouri.loadImage(imageId).promise;
}

async function getDicomInfo(imageId, lang = "zh_CN") {
	// const image = await cornerstoneDICOMImageLoader.wadouri.loadImage(imageId).promise;
	const image = await imageLoader.loadAndCacheImage(imageId);

	// console.log("dicom info:", image);

	const defWL = Math.round(image.windowCenter);
	const defWW = Math.round(image.windowWidth);

	const charset = image.data.string("x00080005") || "ISO_IR 100";
	const isUtf8 = charset.indexOf("ISO_IR 192") != -1;

	let studyInfo = "";
	let val = "";
	let tag = image.data.elements.x00100010; // 姓名
	if (tag) {
		val = readTagAsString(image.data.byteArray, tag.dataOffset, tag.length);
	}
	if (val) {
		val = decodeChinese(val, isUtf8);
		if (config.desensitize) {
			val = desensitizeSubstring(val, 1, -1);
		}
		studyInfo += val + " ";
	}

	val = image.data.string("x00101010");
	if (!val || val.length <= 0) {
		val = image.data.string("x00100030");
		if (val === undefined) val = "";
	} else {
		if (lang == "zh_CN") {
			val = val.replace(/Y|y/, "岁");
			val = val.replace(/M|m/, "月");
			val = val.replace(/D|d/, "天");
		}
	}
	val = val.replace(/^0+/g, "");
	if (val) {
		studyInfo += val + " ";
	}

	val = image.data.string("x00100040");

	if (lang == "zh_CN") {
		if (!val || val.length <= 0) tmp = "未知";
		if (val === "M" || val === "m") val = "男";
		else if (val === "F" || val === "f") val = "女";
		else val = "未知";
	}
	if (val) {
		studyInfo += val;
	}

	return {
		defWL,
		defWW,
		studyInfo
	};
}

export { presetColors, blendModes, ViewportId, getDicomInfo, loadDicom };
